package com.itheima.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie;
import com.itheima.admin.mapper.AdChannelMapper;
import com.itheima.admin.mapper.AdSensitiveMapper;
import com.itheima.admin.pojo.AdChannel;
import com.itheima.admin.pojo.AdSensitive;
import com.itheima.admin.service.WemediaNewsAutoScanService;
import com.itheima.article.dto.ArticleInfoDto;
import com.itheima.article.feign.ApArticleFeign;
import com.itheima.article.pojo.ApArticle;
import com.itheima.article.pojo.ApArticleConfig;
import com.itheima.article.pojo.ApArticleContent;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.util.ali.AliGreenScanner;
import com.itheima.common.vo.ResultVo;
import com.itheima.dfs.feign.DfsFeign;
import com.itheima.wemedia.dto.WmNewsContentNodeDto;
import com.itheima.wemedia.feign.WmNewsFeign;
import com.itheima.wemedia.pojo.WmNews;
import com.itheima.wemedia.vo.WmNewsVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @description 说明
 * @package com.itheima.admin.service.impl
 */
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {

    @Autowired
    private AdSensitiveMapper adSensitiveMapper;

    @Autowired
    private AdChannelMapper adChannelMapper;

    @Autowired
    private WmNewsFeign wmNewsFeign;

    @Autowired
    private DfsFeign dfsFeign;

    @Autowired
    private ApArticleFeign apArticleFeign;

    @Autowired
    private AliGreenScanner aliGreenScanner;

    /**
     * 自媒体文章自动审核
     *
     * @param wmNewsId
     * @param uuid
     */
    @Override
    public void autoScanWemediaNewsById(String wmNewsId, String uuid) {
        //1. 调用自媒体通过id查询文章信息
        Long newsId = Long.parseLong(wmNewsId);
        ResultVo<WmNews> wmNewsResultVo = wmNewsFeign.getWmNewsById(newsId);
        //1.1 判断是事查询到数据
        if (!wmNewsResultVo.isSuccess()) {
            log.error("【出错】文章自动审核：uuid={},id={} 远程调用查询文章失败",uuid,newsId);
            return;
        }
        WmNews wmNews = wmNewsResultVo.getData();
        if(null == wmNews){
            log.error("【出错】文章自动审核：uuid={},id={} 远程调用查询不到文章数据",uuid,newsId);
            return;
        }
        //1.2 文章的状态是否为待审核,只有待审核才需要处理
        if(wmNews.getStatus() != BusinessConstants.WmNewsConstants.STATUS_SUBMIT){
            log.error("【出错】文章自动审核：uuid={},id={} 远程调用查询不到文章状态不一致",uuid,newsId);
            return;
        }
        //2. 文本处理
        //2.1 提取文本内容
        String text = getTextFromWmNews(wmNews);

        WmNews updatePojo = new WmNews();
        updatePojo.setId(wmNews.getId());

        //2.2 敏感词检测
        if(sensitiveWordsCheck(text)){
            // 有敏感词，审核不通过，更新文章的状态
            updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
            updatePojo.setReason("文章内容包含敏感词");
        }else{
            // 3. 反垃圾处理
            // 3.1 文本反垃圾
            try {
                Map<String, String> scanResult = aliGreenScanner.greenTextScan(text);
                String suggestion = scanResult.get("suggestion");
                if(BusinessConstants.ScanConstants.PASS.equals(suggestion)){
                    // 4 没垃圾才要图片反垃圾检测
                    // 4.1 提取 文章中的图片
                    List<String> imageList = getImagesFromWmNews(wmNews);
                    if(CollectionUtils.isNotEmpty(imageList)) {
                        // 4.2 远程调用dfs服务，批量下载图片
                        ResultVo<List<byte[]>> downloadResult = dfsFeign.download(imageList);
                        // 返回 结果判断
                        if (!downloadResult.isSuccess()) {
                            log.error("【出错】文章自动审核：uuid={},id={} 远程调用dfs下载图片失败",uuid,newsId);
                            return;
                        }
                        List<byte[]> imageDataList = downloadResult.getData();
                        // 图片反垃圾
                        scanResult = aliGreenScanner.imageScan(imageDataList);
                        suggestion = scanResult.get("suggestion");
                    }
                }
                // 解析结果
                switch (suggestion){
                    case BusinessConstants.ScanConstants.BLOCK:
                        updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_FAIL);
                        updatePojo.setReason("内容违反相关规定");
                        break;
                    case BusinessConstants.ScanConstants.REVIEW:
                        // 需要人工审核
                        updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_MANUAL);
                        break;
                    case BusinessConstants.ScanConstants.PASS:
                        updatePojo.setStatus(BusinessConstants.WmNewsConstants.STATUS_PASS);
                        break;
                    default:
                        log.error("【出错】文章自动审核：uuid={},id={} 阿里反垃圾返回异常数据",uuid,newsId);
                        return;
                }
            } catch (Exception e) {
                log.error("【出错】文章自动审核：uuid={},id={} 调用阿里反垃圾失败",uuid,newsId,e);
                return;
            }
        }

        // 更新文章的状态
        ResultVo<WmNews> updateResult = wmNewsFeign.update(updatePojo);
        if(updateResult.isSuccess()){
            log.info("文章自动审核结果更新成功：uuid={},id={},status={}",uuid,newsId, updatePojo.getStatus());
        }else{
            log.error("【出错】文章自动审核：文章自动审核结果更新失败：uuid={},id={},status={}",uuid,newsId, updatePojo.getStatus());
        }
    }

    /**
     * 同步文章数据
     *
     * @param wmNewsVo
     * @return
     */
    @Override
    public boolean syncArticleInfo(WmNewsVo wmNewsVo) {
        //1. 构建articleInfoDto
        ArticleInfoDto dto = new ArticleInfoDto();
        //2. 构建文章信息apArticle
        ApArticle apArticle = buildApArticle(wmNewsVo);
        dto.setApArticle(apArticle);

        //3. 构建文章内容对象apArticleContent
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setContent(wmNewsVo.getContent());
        dto.setApArticleContent(apArticleContent);
        //4. 构建文章配置对象apArticleConfig
        ApArticleConfig apArticleConfig = new ApArticleConfig();
        apArticleConfig.setIsDown(0);// 初始化为非下架
        apArticleConfig.setIsDelete(0);// 0：没被删除
        apArticleConfig.setIsForward(1);
        apArticleConfig.setIsComment(1);
        dto.setApArticleConfig(apArticleConfig);
        //5. 自媒体用户
        dto.setWmUserId(wmNewsVo.getUserId());
        // app用户id 作者
        dto.setApUserId(wmNewsVo.getApUserId());
        // 自媒体文章id(更新状态)
        dto.setWmNewsId(wmNewsVo.getId());
        //6. 远程调用文章微服务保存文章信息
        ResultVo resultVo = apArticleFeign.saveArticleInfo(dto);
        //7. 解析返回结果
        return resultVo.isSuccess();
    }

    private ApArticle buildApArticle(WmNewsVo wmNewsVo) {
        ApArticle apArticle = new ApArticle();
        // 复制属性
        BeanUtils.copyProperties(wmNewsVo, apArticle);

        //TODO 同步文章修改文章时，需要先删除再新增

        // 修改文章时，删除旧有的已发布文章
        // 第一次同步发布文章时，wmNewsVo.getArticleId()为null，此时新增操作
        // 后续同步发布文章时，wmNewsVo.getArticleId()有id数据，此时要先删除旧有已发布文章，再新增
        apArticle.setId(wmNewsVo.getArticleId());
        // 作者id
        apArticle.setAuthorId(wmNewsVo.getApUserId());
        // 作者名称
        apArticle.setAuthorName(wmNewsVo.getAuthorName());
        // 频道名称
        Integer channelId = wmNewsVo.getChannelId();
        AdChannel adChannel = adChannelMapper.selectById(channelId);
        apArticle.setChannelName(adChannel.getName());
        // 布局
        apArticle.setLayout(wmNewsVo.getType());
        apArticle.setFlag(0);//普通文章
        apArticle.setLikes(0);
        apArticle.setCollection(0);
        apArticle.setComment(0);
        apArticle.setViews(0);
        apArticle.setProvinceId(0);
        apArticle.setCityId(0);
        apArticle.setCountyId(0);
        apArticle.setSyncStatus(0);//0:未同步给es
        apArticle.setOrigin(1);//1:自媒体，2：爬虫
        return apArticle;
    }

    /**
     * 提取 文章中的图片
     * @param wmNews
     * @return
     */
    private List<String> getImagesFromWmNews(WmNews wmNews) {
        String content = wmNews.getContent();
        // 解析
        List<WmNewsContentNodeDto> contentNodeList = JSON.parseArray(content, WmNewsContentNodeDto.class);
        // stream()遍历contentNodeList, 取它每个元素来处理, filter条件满足true，则留下
        List<String> imageList = contentNodeList.stream().filter(node -> "image".equals(node.getType())).map(WmNewsContentNodeDto::getValue)
            .collect(Collectors.toList());
        // 添加封面, 封面多张时是以逗号分割
        String images = wmNews.getImages();
        if(StringUtils.isNotEmpty(images)){
            // 有封面
            String[] split = images.split(",");
            Collections.addAll(imageList, split);
        }
        return imageList;
    }

    /**
     * 敏感词检测
     * @param text
     * @return
     */
    private boolean sensitiveWordsCheck(String text) {
        //1. 查询数据设置的敏感词
        // queryWrapper.select(只查询这里指定的列)
        QueryWrapper<AdSensitive> queryWrapper = Wrappers.<AdSensitive>query().select("sensitives");
        List<AdSensitive> sensitiveList = adSensitiveMapper.selectList(queryWrapper);
        //2. 构建一个map
        if(CollectionUtils.isEmpty(sensitiveList)){
            return false; // 没有敏感词
        }
        Map<String, String> map = sensitiveList.stream().collect(Collectors.toMap(AdSensitive::getSensitives, AdSensitive::getSensitives));
        //3. 构建字典
        AhoCorasickDoubleArrayTrie<String> trie = new AhoCorasickDoubleArrayTrie<String>();
        trie.build(map);
        //4. 检测
        AhoCorasickDoubleArrayTrie.Hit<String> first = trie.findFirst(text);
        //5. 返回结果
        return first!=null;
    }

    /**
     * 提取文本内容
     * @param wmNews
     * @return
     */
    private String getTextFromWmNews(WmNews wmNews) {
        String content = wmNews.getContent();
        // 解析
        List<WmNewsContentNodeDto> contentNodeList = JSON.parseArray(content, WmNewsContentNodeDto.class);
        // stream()遍历contentNodeList, 取它每个元素来处理, filter条件满足true，则留下
        List<String> textList = contentNodeList.stream().filter(node -> "text".equals(node.getType())).map(WmNewsContentNodeDto::getValue)
            .collect(Collectors.toList());
        // 添加标题
        textList.add(wmNews.getTitle());
        // 以逗号分割
        return String.join(",", textList);
    }
}
